<?php

namespace App\Controller;

use App\Constants\BlackTypeEnum;
use App\Constants\ErrorCode;
use App\Model\Business\Application;
use App\Model\Business\BlackDaily;
use App\Model\Business\BlackIp;
use App\Model\Business\BlackOaid;
use App\Request\Launch\InitInfoReq;
use App\Services\Impl\CommonService;
use App\Services\Impl\ReportService;
use App\Utils\EncryptUtil;
use App\Utils\ProjectUtil;
use Hyperf\Di\Annotation\Inject;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Http\Message\ResponseInterface;

class LaunchController extends AbstractController
{
    #[Inject]
    protected CommonService $commonService;

    #[Inject]
    protected ReportService $reportService;

    /**
     * App启动首次初始化
     * @param InitInfoReq $req
     * @return ResponseInterface
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function initInfo(InitInfoReq $req): ResponseInterface
    {
        $params      = $req->all();
        $appId       = $params['appId'];
        $channel     = $params['channel'];
        $version     = $params['version'];
        $hasProxy    = $params['hasProxy'];
        $hasSimCard  = $params['hasSimCard'];
        $hasVpn      = $params['hasVpn'];
        $oaid        = $params['oaid'];
        $currentTime = $params['currentTime'];
        $model       = $params['model'];

        $app = Application::findOrFail($appId);

        // 调用启动逻辑
        $this->reportService->reportLaunch($app->project_id, $appId, $channel, $version, $oaid);

        $businessId = $this->commonService->getBusinessId();
        $sessionId  = ProjectUtil::generateRandomString();

        $returnData = [
            // 黑名单类型
            'blackType'      => BlackTypeEnum::NO->value,
            // 是否合规，合规先弹隐私，不合规主页弹隐私
            'isCompliance'   => false,
            // 是否归因用户
            'isAscribe'      => false,
            // 归因渠道
            'ascribeChannel' => $channel,
            // 隐私ID
            'businessId'     => $businessId,
            // 会话ID
            'sessionId'      => $sessionId
        ];

        $diffClientTime = abs(time() - $currentTime);

        // 如果是001渠道，并且客户端本地修改了时间，则进黑名单
        if ($diffClientTime >= 60 && $channel == '001') {
            $this->commonService->insertBlackDaily($params);
            $returnData['blackType'] = BlackTypeEnum::CLIENT_CHANGE_TIME->value;
            return $this->succeed($returnData);
        }

        // 如果开代理了，黑名单
        if ($hasProxy) {
            $this->commonService->insertBlackDaily($params);
            $returnData['blackType'] = BlackTypeEnum::OPEN_PROXY->value;
            return $this->succeed($returnData);
        }

        // 如果没插卡，黑名单
        if (!$hasSimCard) {
            $this->commonService->insertBlackDaily($params);
            $returnData['blackType'] = BlackTypeEnum::NOT_SIM_CARD->value;
            return $this->succeed($returnData);
        }

        // 如果开VPN了，黑名单
        if ($hasVpn) {
            $this->commonService->insertBlackDaily($params);
            $returnData['blackType'] = BlackTypeEnum::OPEN_VPN->value;
            return $this->succeed($returnData);
        }

        // 指定型号黑名单
        if ($this->commonService->isSpecificModelBlack($model, $channel)) {
            $this->commonService->insertBlackDaily($params);
            $returnData['blackType'] = BlackTypeEnum::SPECIFIC_MODEL->value;
            return $this->succeed($returnData);
        }

        $adSwitchRet = $this->commonService->getAdSwitch($appId, $channel, $version);
        $adSwitch    = $adSwitchRet['adSwitch'];

        // 如果广告关了被关了
        if ($adSwitch == 2) {
            $this->commonService->insertBlackDaily($params);
            $returnData['blackType'] = BlackTypeEnum::AD_CLOSE->value;
            return $this->succeed($returnData);
        }

        $ip = $this->commonService->getClientIp();

        // IP 永久黑名单
        $isBlackIp = BlackIp::findBlackIp($channel, $ip);
        if ($isBlackIp) {
            $this->commonService->insertBlackDaily($params);
            $returnData['blackType'] = BlackTypeEnum::IP->value;
            return $this->succeed($returnData);
        }

        // OAID 永久黑名单
        $isBlackOaid = BlackOaid::findBlackOaid($channel, $oaid);
        if ($isBlackOaid) {
            $this->commonService->insertBlackDaily($params);
            $returnData['blackType'] = BlackTypeEnum::OAID_PERMANENT->value;
            return $this->succeed($returnData);
        }

        // OAID 临时黑名单
        $dailyBlackOaid = BlackDaily::findTodayByOaid($channel, $oaid);
        if ($dailyBlackOaid) {
            $this->commonService->insertBlackDaily($params);
            $returnData['blackType'] = BlackTypeEnum::OAID_TODAY->value;
            return $this->succeed($returnData);
        }

        $ascribeRet = $this->commonService->getIsAscribeUser($appId, $channel, $oaid);

        // 是否是归因用户
        $ascribeInfo                  = $this->commonService->getAscribeInfo($appId, $oaid);
        $returnData['isAscribe']      = $ascribeInfo['isAscribe'];
        $returnData['ascribeChannel'] = $ascribeInfo['ascribeChannel'];

        return $this->succeed($returnData);
    }

    /**
     * App首次启动二次初始化
     * @param InitInfoReq $req
     * @return ResponseInterface
     */
    public function secondInitInfo(InitInfoReq $req): ResponseInterface
    {
        $params      = $req->all();
        $appId       = $params['appId'];
        $channel     = $params['channel'];
        $version     = $params['version'];
        $hasProxy    = $params['hasProxy'];
        $hasSimCard  = $params['hasSimCard'];
        $hasVpn      = $params['hasVpn'];
        $oaid        = $params['oaid'];
        $currentTime = $params['currentTime'];

        $ip = $this->commonService->getClientIp();

        $returnData = [
            'ascribeChannel' => ''
        ];

        // OAID 永久黑名单
        $isBlackOaid = BlackOaid::findBlackOaid($channel, $oaid);
        if ($isBlackOaid) {
            $this->commonService->insertBlackDaily($params);
            $returnData['blackType'] = BlackTypeEnum::OAID_PERMANENT->value;
            return $this->succeed($returnData);
        }

        // OAID 临时黑名单
        $dailyBlackOaid = BlackDaily::findTodayByOaid($channel, $oaid);
        if ($dailyBlackOaid) {
            $this->commonService->insertBlackDaily($params);
            $returnData['blackType'] = BlackTypeEnum::OAID_TODAY->value;
            return $this->succeed($returnData);
        }

        // 是否是归因用户
        $ascribeInfo                  = $this->commonService->getAscribeInfo($appId, $oaid);
        $returnData['ascribeChannel'] = $ascribeInfo['ascribeChannel'];

        return $this->succeed($returnData);
    }

    /**
     * 获取加密示例，前端可以使用这个加密后的数据进行测试
     */
    public function encryptExample(): ResponseInterface
    {
        // 示例数据
        $data = [
            'projectId' => 123,
            'appClient' => 'android',
            'userId'    => 10001,
            'extra'     => [
                'version' => '1.0.0',
                'channel' => 'official'
            ]
        ];

        // 加密数据（使用设备ID作为自定义盐值）
        $deviceId      = 'test_device_123'; // 在实际应用中，这应该是前端传过来的设备唯一标识
        $encryptedData = EncryptUtil::encrypt($data, $deviceId);

        // 示例解密数据
        $decryptedData = EncryptUtil::decrypt($encryptedData, $deviceId);

        // 返回加密后的数据和使用说明
        return $this->succeed([
            'encrypted_data'           => $encryptedData,
            'decrypted_data'           => $decryptedData,  // 仅用于演示
            'usage_example'            => [
                'request_headers' => [
                    'X-Device-Id'  => $deviceId,
                    'Content-Type' => 'application/json',
                ],
                'request_body'    => [
                    'encrypted_data' => $encryptedData
                ],
                'curl_example'    => 'curl -X POST -H "Content-Type: application/json" -H "X-Device-Id: ' . $deviceId . '" -H "X-Encrypt-Response: true" -d \'{"encrypted_data":"' . $encryptedData . '"}\' http://your-domain/launch/init-info',
                'note'            => '前端需要在请求头中添加设备标识符，并将所有参数加密后作为encrypted_data字段发送。如需加密响应，添加X-Encrypt-Response头'
            ],
            'frontend_implementation'  => [
                'step1'                 => '使用相同的加密算法在前端实现数据加密',
                'step2'                 => '将设备标识符（如设备ID、IMEI等）作为请求头发送',
                'android_security_tips' => [
                    'tip1' => '使用NDK实现加密算法，将关键算法编译为so库',
                    'tip2' => '使用代码混淆和反调试技术保护密钥',
                    'tip3' => '密钥分段存储，不要存储在单一位置',
                    'tip4' => '考虑使用设备指纹（如设备ID、硬件特征等）作为加密盐值',
                    'tip5' => '实现APP完整性校验机制，防止被篡改',
                    'tip6' => '响应数据解密逻辑也应放在NDK中实现'
                ]
            ],
            'response_encryption_demo' => [
                'sample_encrypted_response' => [
                    'encrypted' => true,
                    'data'      => '...(加密后的数据字符串)...'
                ],
                'decryption_process'        => '使用与请求加密相同的解密方法对data字段进行解密'
            ]
        ]);
    }
}